package com.xquant.pricing.calc.service.impl.otcOption;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSONArray;
import com.xquant.common.core.constant.ErrMsgConstant;
import com.xquant.common.core.exception.ServiceException;
import com.xquant.common.security.utils.SecurityUtils;
import com.xquant.pricing.calc.entity.ProductTypeEnum;
import com.xquant.pricing.calc.entity.RequestCodeEnum;
import com.xquant.pricing.calc.service.CalcCommonService;
import com.xquant.pricing.pricingConfig.mapper.EngineConfigMapper;
import com.xquant.pricing.pricingConfig.mapper.OptionContractMapper;
import com.xquant.pricing.pricingConfig.service.ComboBoxService;
import com.xquant.pricing.utils.CacheUtil;
import com.xquant.pricing.utils.DateUtils;
import com.xquant.common.engine.xPP.request.*;
import com.xquant.common.engine.xPP.response.CalcResult;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Node;
import org.dom4j.XPath;
import org.springframework.beans.factory.annotation.Autowired;

import java.sql.SQLException;
import java.util.*;

public abstract class CalcCommonService4OtcOption implements CalcCommonService {
    @Override
    public String[] getSupportedProductType() {
        return new String[]{
                ProductTypeEnum.OTC_OPTION.getCode()
        };
    }

    List<Map<String, Object>> contractList = new ArrayList<>();
    List<Map<String, Object>> engineList = new ArrayList<>();
    List<Map<String, Object>> cfgList = new ArrayList<>();


    @Autowired
    protected EngineConfigMapper engineConfigMapper;
    @Autowired
    protected ComboBoxService comboBoxService;
    @Autowired
    private OptionContractMapper contractMapper;

    /**
     * 合并参数，理论上，有cfgid，使用cfgid的参数，标的价格使用传入的。
     *
     * @param calcPricingParamDefault
     * @param calcPricingParamNew
     * @return
     */
    @Override
    public CalcEngineParam mergeCalcPricingParam(CalcEngineParam calcPricingParamDefault, CalcEngineParam calcPricingParamNew) {
        // 仅更新计算日、标的价格
        if (calcPricingParamNew.getCalcPricingParam().getValueDate() != null) {
            calcPricingParamDefault.getCalcPricingParam().setValueDate(calcPricingParamNew.getCalcPricingParam().getValueDate());
        } else {
            calcPricingParamDefault.getCalcPricingParam().setValueDate(DateUtils.getCurrentDate());
        }
        if (calcPricingParamNew.getCalcPricingParam().getUnderlyingPricingParams() != null &&
                calcPricingParamNew.getCalcPricingParam().getUnderlyingPricingParams().get(0) != null //参数可能传入[null]，导致此处报错
                && calcPricingParamDefault.getCalcPricingParam().getUnderlyingPricingParams() != null//现金流无标的定价参数，现金流参数新格式，定盘价信息在underlyingMarkets节点，与calcPricingParams同级
        ) {
            calcPricingParamNew.getCalcPricingParam().getUnderlyingPricingParams().forEach((item) -> {
                for (int i = 0; i < calcPricingParamDefault.getCalcPricingParam().getUnderlyingPricingParams().size(); i++) {
                    if (calcPricingParamDefault.getCalcPricingParam().getUnderlyingPricingParams().get(i).getUnderlyingID().equals(item.getUnderlyingID())) {
                        calcPricingParamDefault.getCalcPricingParam().getUnderlyingPricingParams().get(i).setSpotPrice(item.getSpotPrice());
                        calcPricingParamDefault.getCalcPricingParam().getUnderlyingPricingParams().get(i).setSpotQuoteType(item.getSpotQuoteType());
                    }
                }
            });
        }
        return calcPricingParamDefault;
    }

    @Override
    public String getDefaultCfgId(String cfgId) {
        List<String> listCfg = new ArrayList<>();
        // cfgId为空时，获取默认定价参数
        Object uId = SecurityUtils.getUserId();
        Map<String, Object> params = new WeakHashMap<>();
        params.put("userId", uId);
        params.put("isAuthor", "true");
        params.put("productType", ProductTypeEnum.OTC_OPTION.getCode());
        if (StringUtils.isBlank(cfgId)) {
            if (CacheUtil.getInstance().getCacheData(SecurityUtils.getUserId()+"listCfg") != null) {
                listCfg = (List<String>) CacheUtil.getInstance().getCacheData(SecurityUtils.getUserId()+"listCfg");
            }else{
                params.put("isDefault", "1");
                params.put("isAuth", "false");
                cfgList = comboBoxService.queryPriceCfg(params);
                if (cfgList == null || cfgList.size() == 0) {
                    return null;
                }
                //20221021 botao.yu 以用户为单位 存入默认定价配置
                CacheUtil.getInstance().addCacheData(MapUtils.getString(cfgList.get(0), "cfgId"), cfgList.get(0));
                listCfg.add(MapUtils.getString(cfgList.get(0), "cfgId"));
                CacheUtil.getInstance().addCacheData(SecurityUtils.getUserId()+"listCfg", listCfg);
            }
            cfgId =listCfg.get(0);
        } else {
            if (CacheUtil.getInstance().getCacheData("listCfg") != null) {
                listCfg = (List<String>) CacheUtil.getInstance().getCacheData("listCfg");
            }
            if (listCfg.contains(cfgId)) {
                cfgList = (List<Map<String, Object>>) CacheUtil.getInstance().getCacheData(cfgId);
                if (cfgList == null) {
                    cfgList = new ArrayList<>();
                }
            } else {
                cfgList = comboBoxService.queryPriceCfg(params);
                CacheUtil.getInstance().addCacheData(cfgId, cfgList);
                listCfg.add(cfgId);
                CacheUtil.getInstance().addCacheData("listCfg", listCfg);
            }
            if (listCfg == null || listCfg.size() == 0) {
                return null;
            }
        }
        return cfgId;
    }

    public Map<String, Object> getContractCfg(Instrument instrument, String cfgId, List<String> underlyIds) {
        List<String> listContract = new ArrayList<>();
        // 金融工具解析
        String productType = ObjectUtil.isNull(instrument.getInitProductType()) ? instrument.getProductType() : instrument.getInitProductType();
        if (StringUtils.isBlank(instrument.getFpml())) {
            if (instrument.getTermsheet().getJSONArray("customLegs") == null) {
                //雪球特殊处理，取子类型 sun.js
                if (productType.equals("Autocallable")) {
                    productType = instrument.getTermsheet().getString("productType");
                } else {
                    productType = instrument.getProductType();
                }
            }
        }
        String id = instrument.getId();
        StringBuffer contract = new StringBuffer();
        // 合约定价参数
        Map<String, Object> param = new WeakHashMap<>();
        param.put("cfgId", cfgId);
        if (StringUtils.isNotBlank(productType)) {
            param.put("productType", productType);
        } else {
            param.put("productType", "*");
        }
        if (underlyIds.size() > 0) {
            String[] item = underlyIds.get(0).split(",");
            param.put("uICode", item[0]);
            param.put("uAType", item[1]);
            param.put("uMType", item[2]);
        } else {
            param.put("uICode", "*");
            param.put("uAType", "*");
            param.put("uMType", "*");
        }
        contract.append(param.get("productType")).append(param.get("uICode")).append(param.get("uAType")).append(param.get("uMType"));

        if (CacheUtil.getInstance().getCacheData("listContract") != null) {
            listContract = (List<String>) CacheUtil.getInstance().getCacheData("listContract");
        }
        if (listContract.contains(contract.toString())) {
            contractList = (List<Map<String, Object>>) CacheUtil.getInstance().getCacheData("ContractCfg" + contract.toString());
            if (contractList == null) {
                contractList = new ArrayList<>();
            }
        } else {

            contractList = contractMapper.findContractCfgById(param);
            CacheUtil.getInstance().addCacheData("ContractCfg" + contract.toString(), contractList);
            listContract.add(contract.toString());
            CacheUtil.getInstance().addCacheData("listContract", listContract);

        }
        Map<String, Object>[] arrays = new Map[7];
        //System.out.println(contract.toString());
        String switchProductType = switchProductType(MapUtils.getString(param, "productType"));
        for (Map<String, Object> contractCfg : contractList) {
            if (StringUtils.equals(MapUtils.getString(contractCfg, "I_CODE"), id)) {
                arrays[0] = contractCfg;
            } else if (//匹配期权类型，标的
                    StringUtils.equals(MapUtils.getString(contractCfg, "I_CODE"), "*") &&
                            MapUtils.getString(contractCfg, "PRODUCT_TYPE").indexOf(switchProductType) > -1 &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_I_CODE"), MapUtils.getString(param, "uICode")) &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_A_TYPE"), MapUtils.getString(param, "uAType")) &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_M_TYPE"), MapUtils.getString(param, "uMType"))
            ) {
                arrays[1] = contractCfg;
            } else if (//匹配期权类型，资产类型
                    StringUtils.equals(MapUtils.getString(contractCfg, "I_CODE"), "*") &&
                            MapUtils.getString(contractCfg, "PRODUCT_TYPE").indexOf(switchProductType) > -1 &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_I_CODE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_A_TYPE"), MapUtils.getString(param, "uAType")) &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_M_TYPE"), "*")
            ) {
                arrays[2] = contractCfg;
            } else if (//匹配期权类型
                    StringUtils.equals(MapUtils.getString(contractCfg, "I_CODE"), "*") &&
                            MapUtils.getString(contractCfg, "PRODUCT_TYPE").indexOf(switchProductType) > -1 &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_I_CODE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_A_TYPE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_M_TYPE"), "*")
            ) {
                arrays[3] = contractCfg;
            } else if (//匹配标的
                    StringUtils.equals(MapUtils.getString(contractCfg, "I_CODE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "PRODUCT_TYPE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_I_CODE"), MapUtils.getString(param, "uICode")) &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_A_TYPE"), MapUtils.getString(param, "uAType")) &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_M_TYPE"), MapUtils.getString(param, "uMType"))
            ) {
                arrays[4] = contractCfg;
            } else if (//匹配资产类型
                    StringUtils.equals(MapUtils.getString(contractCfg, "I_CODE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "PRODUCT_TYPE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_I_CODE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_A_TYPE"), MapUtils.getString(param, "uAType")) &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_M_TYPE"), "*")
            ) {
                arrays[5] = contractCfg;
            } else if (//无匹配
                    StringUtils.equals(MapUtils.getString(contractCfg, "I_CODE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "PRODUCT_TYPE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_I_CODE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_A_TYPE"), "*") &&
                            StringUtils.equals(MapUtils.getString(contractCfg, "U_M_TYPE"), "*")
            ) {
                arrays[6] = contractCfg;
            }
        }
        //按照顺序遍历，最先有数据的，优先级最高，直接返回
        for (Map<String, Object> result : arrays) {
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    private String switchProductType(String productType) {
        switch (productType) {
            case "KnockOut":
            case "KnockIn":
            case "DoubleKnockOut":
            case "DoubleKnockIn":
                return "Barrier";
//                return "DoubleKnock";
            case "NoTouch":
            case "OneTouch":
            case "DoubleNoTouch":
            case "DoubleOneTouch":
                return "Touch";
//                return "DoubleTouch";
            default:
                return productType;
        }
    }

    @Override
    public Boolean isUseDefaultPricingParam(CalcEngineParam param, String requestCode) {
        if (RequestCodeEnum.CALC_MARKET.getCode().equals(requestCode)) {
            return param.getCalcPricingParam().getDiscount() == null || param.getCalcPricingParam().getDiscount().checkIrNull();
        } else if (RequestCodeEnum.CALC_PAYMENTINFO.getCode().equals(requestCode)) {
            return StringUtils.isBlank(param.getCalcPricingParam().getCalcPPCode());
        } else if (RequestCodeEnum.READ_FIXINGDATES.getCode().equals(requestCode)) {
            return StringUtils.isBlank(param.getCalcPricingParam().getCalcPPCode());
        } else if (RequestCodeEnum.CALC_SCENE.getCode().equals(requestCode)) {
            return StringUtils.isBlank(param.getCalcPricingParam().getCalcPPCode());
        }
        return false;
    }

    @Override
    public CalcResult beforeCalc(CalcEngineParam param) {
        // 如果是自定义期权，productType要改成CustomOption
        String productType = param.getInstrument().getProductType();
//        switch (productType) {
//            case "Vanilla":
//            case "Digital":
//            case "Barrier":
//            case "Touch":
//            case "Autocallable":
//            case "Asian":
//            case "RangeAccrual":
//            case "CustomOption":
//            case "Premium":
//            case "KnockOut":
//            case "KnockIn":
//            case "DoubleBarrier":
//            case "DoubleKnockOut":
//            case "DoubleKnockIn":
//            case "NoTouch":
//            case "OneTouch":
//            case "DoubleNoTouch":
//            case "DoubleOneTouch":
//            case "QuantoVanilla":
//            case "ELN_ACB_PHOENIX":
//            case "ELN_ACB_STANDARDSNOWBALL":
//            case "ELN_ACB_BASICSNOWBALL":
//            case "ELN_ACB_KNOCKIN_VANILLA":
//            case "ELN_ACB_VANILLASPREAD":
//            case "ELN_ACB_FLOOREDSNOWBALL":
//            case "SingleSharkFin":
//            case "IRStreams":
//            case "UncappedAirbag":
//            case "BoostedAirbag":
//            case "CappedAirbag":
//            case "MultilayerDigital":
//                break;
//            default:
//                param.getInstrument().setProductType("CustomOption");
//                break;
//        }
        // 校验计算组件
        CalcResult calcResult = checkPPCompoment(param, productType);
        resolveIrFwdPricingParam(param);
        return calcResult;
    }

    private CalcResult checkPPCompoment(CalcEngineParam param, String productType) {
        List<String> listEngine = new ArrayList<>();
        CalcPricingParam calcPricingParam = param.getCalcPricingParam();
        String calcPPCode = calcPricingParam.getCalcPPCode();
        Map queryParams = new HashMap();
        queryParams.put("ppCode", calcPPCode);
        if (CacheUtil.getInstance().getCacheData("listEngine") != null) {
            listEngine = (List<String>) CacheUtil.getInstance().getCacheData("listEngine");
        }

        if (listEngine.contains(calcPPCode)) {
            engineList = (List<Map<String, Object>>) CacheUtil.getInstance().getCacheData(calcPPCode);
            if (engineList == null) {
                engineList = new ArrayList<>();
            }
        } else {
            try {
                engineList = engineConfigMapper.queryEngineConfig(queryParams);
            } catch (SQLException e) {
                throw new ServiceException(ErrMsgConstant.QUERY_ERRINFO,e.getMessage());
            }
            CacheUtil.getInstance().addCacheData(calcPPCode, engineList);
            listEngine.add(calcPPCode);
            CacheUtil.getInstance().addCacheData("listEngine", listEngine);
        }
        boolean ifSupport = engineList.stream().filter(item -> {
            String products = String.valueOf(item.get("products"));
            return products.contains(productType);
        }).findAny().isPresent();

        CalcResult calcResult = new CalcResult();
        calcResult.setErrorCode("0");
        if (!ifSupport) {
            calcResult.setErrorCode("-1");
            calcResult.setErrorMsg("选择的插件不能支持此种产品类型的场外期权");
        }
        return calcResult;
    }

    /**
     * 解析利率场外期权标的定价参数远期覆盖红利
     * 含FPML的请求，优先使用红利率，期次远期阀盖红利率
     * json格式的金融工具的条款，必须传入underlyingPricingParam.UnderlyingID,根据a_type判断是否使用远期覆盖红利，否则不处理
     *
     * @param param
     */
    private void resolveIrFwdPricingParam(CalcEngineParam param) {
        List<String> aTypes = CollectionUtil.newArrayList("SPT_REPO", "SPT_TMD", "SPT_IBOR", "SPT_DED", "SPT_NGD", "SPT_NTD", "SPT_LN", "SPT_CF");

        if (param.getCalcPricingParam().getUnderlyingPricingParams() == null) {
            return;
        }
        for (UnderlyingPricingParam u : param.getCalcPricingParam().getUnderlyingPricingParams()) {
            String instrumentId = u.getUnderlyingID();
            if (StringUtils.isNotBlank(instrumentId)) {
                String[] instrument = instrumentId.split(",");
                if (aTypes.contains(instrument[1]) && u.getForward() != null) {
                    Ir dividend = new Ir();
                    dividend.setCode(u.getForward().getCode());
                    u.setDividend(dividend);
                }
            }
            u.setForward(null);
        }
    }

    @Override
    public void afterCalc(CalcResult param) {

    }

    protected List<String> getUnderlyIds(Instrument instrument) {
        if (StringUtils.isBlank(instrument.getFpml())) {
            List<String> list = new ArrayList<>();
            JSONArray underlyingIds = instrument.getTermsheet().getJSONArray("underlyerIds");
            for (int i = 0; i < underlyingIds.size(); i++) {
                if (underlyingIds.get(i) != null) {
                    list.add(underlyingIds.get(i).toString());
                }
            }
            return list;
        } else {
            return getUnderlyIdFromFpml(instrument.getFpml());
        }
    }

    protected List<String> getUnderlyIdFromFpml(String fpml) {
        try {
            Document document = DocumentHelper.parseText(fpml);
            XPath xPath = document.createXPath("//xmlns:instrumentId");
            Map<String, String> names = new WeakHashMap<String, String>();
            names.put("xmlns", document.getRootElement().getNamespaceURI());
            xPath.setNamespaceURIs(names);
            List<Node> nodes = xPath.selectNodes(document);
            List<String> list = new ArrayList<>();
            for (Node node : nodes) {
                list.add(node.getText());
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            return ListUtil.empty();
        }
    }
}
